2021_glass_music_in_similar_motion.py

#

SPDX-FileCopyrightText: 2021 Gaston Demoulin & Loïc Mabire SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be

SPDX-License-Identifier: GPL-3.0-or-later

#

Music in similar motion - Philip Glass 1969 # # final script- 25/01/2020 - Loïc M - Gaston D # Blender 2.93.4 #

import bpy
import random
import bmesh
import math
#
def clean():  # cleanup fonction
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.delete(use_global=False)
    bpy.ops.outliner.orphans_purge()
#
def curve(measure, coords):
    curveData = bpy.data.curves.new(measure, type="CURVE")
    curveData.dimensions = "3D"
    curveData.resolution_u = 20
    polyline = curveData.splines.new("NURBS")
    polyline.points.add(len(coords) - 1)

    for i, coord in enumerate(coords):
        x, y, z = coord
        polyline.points[i].co = (x, y, z, 1)

    curveOB = bpy.data.objects.new("mesure", curveData)
    curveData.extrude = 0.1
    myCollection = bpy.context.collection
    myCollection.objects.link(curveOB)
    bpy.data.scenes[0].collection.objects.link(curveOB)
    return curveOB
#
def cube(position, echelle):
    bpy.ops.mesh.primitive_cube_add(
        size=1, enter_editmode=False, location=position, scale=echelle
    )
    bpy.ops.context.rename
#
def get_objects(name):
    res = []
    for obj in bpy.data.objects:
        if name in obj.name:
            res.append(obj)
    return res
#
def dot():
    print("-----")
    print("-----")


clean()
#

innit variable

listeZ = [-1, 1]  # possibilities on z
listeX = [2, 3, 4]  # possibilities on x
newco = []
sizex = 1
nbrepet = 0
nbadd = 0
list = [1, 0, 0, 0, 0, 0]
startInstrument = [0]
list_EP = []
facteur_EP = []
offsetrandom = []
ibis = -1
addX = 0
addX2 = 0
#

strat by writing the first measure as coordinates

coord_list = [
    [0, 0, 0],
    [0, 0, 0],
    [0, 0, 0],
    [random.choice(listeX), 0, random.choice(listeX)],
]  # the first measure always start at 0,0,0
for i in range(0, 1):
    coord_list.append(
        [
            coord_list[3 + i][0] + random.choice(listeX),
            0,
            coord_list[3 + i][2] + random.choice(listeZ),
        ]
    )
for coordfin in range(0, 2):
    coord_list.append(coord_list[-1])
coord_list_bis = coord_list
coord_list_prec = coord_list
#

choice start instrument

nbinstrument = random.randint(3, 5)

if nbinstrument >= 4:
    var = 1.5
else:
    var = 2.5
for a in range(0, nbinstrument):
    facteur_EP.append(var * random.uniform(0.75, 1.25))
    offsetrandom.append(random.uniform(-0.2, 0.2))


print("start intrumentbis")
print(nbinstrument)
print(startInstrument)
#

strat writing for console reading

dot()
print("start")
dot()
#
              curve                     #
#

main loop writing the 14 measure of the piece as an evolutive coord_list

for i in range(
    0, 14
):  # all the rules writing the curves are stats from the original piece
#

each new coord is added randomly within the actual list at ‘index’, keeping the first and last coords the same

    index = random.randint(3, len(coord_list) - 4)  # choose one coordinate in the list
    prevx, prevy, prevz = coord_list[index]  # read the coordinate at index
#

14% chance of adding 0 coordinate, 50% of adding one, 36% of adding 2

    resultchoice = random.randint(1, 100)  # get the % for coord choice
#

reset variables

    addX = 0
    addX2 = 0
    choice0 = False
    choice3 = False
#

console reading

    dot()
    print("iteration", i + 1)
    print("coord_liststart", coord_list)
    print("index choosed", index, ":", coord_list[index])

    if resultchoice <= 14:  # 14% -  add 0 coordinate
        print("add 0")
        nbadd = 0
        choice0 = True
    elif resultchoice <= 64:  # 50% -  add 1 coordinate
        print("add 1")
        nbadd = 2
#

with the index coordinate we find the next one within the 6 possibilities:

        addX = random.choice(listeX)
        newX = prevx + addX
        newZ = prevz + random.choice(listeZ)
#

doing it twice

        if (
            newZ < -2
            or coord_list[index - 2][2]
            > coord_list[index - 1][2]
            > coord_list[index][2]
        ):
            newZ = newZ + 1
        elif (
            newZ > 4
            or coord_list[index - 2][2]
            < coord_list[index - 1][2]
            < coord_list[index][2]
        ):
            newZ = newZ - 1

    else:  # 36% -  add 2 coordinate
        print("add 2")
        choice3 = True
        nbadd = 3
#

with the index coordinate we find the next one within the 6 possibilities:

        addX = random.choice(listeX)
        newX = prevx + addX
        newZ = prevz + random.choice(listeZ)
#

bounding box

        if (
            newZ < -2
            or coord_list[index - 2][2]
            > coord_list[index - 1][2]
            > coord_list[index][2]
        ):
            newZ = newZ + 1
        elif (
            newZ > 4
            or coord_list[index - 2][2]
            < coord_list[index - 1][2]
            < coord_list[index][2]
        ):
            newZ = newZ - 1
#

doing it twice

        addX2 = random.choice(listeX)
        newX2 = newX + addX
        newZ2 = newZ + random.choice(listeZ)
#

bounding box

        if (
            newZ < -2
            or coord_list[index - 2][2]
            > coord_list[index - 1][2]
            > coord_list[index][2]
        ):
            newZ = newZ + 1
        elif (
            newZ > 4
            or coord_list[index - 2][2]
            < coord_list[index - 1][2]
            < coord_list[index][2]
        ):
            newZ = newZ - 1
#

one in 5 chance to delete the first note of the measure

    if 9 > len(coord_list) > 13:
        chancesuppr = [1, 2, 3, 4, 5]
        resultatsuprr = random.choice(chancesuppr)
        if resultatsuppr == 3:
            print("SUPPR", coord_list[4, 5])
            del coord_list[4, 5]

    coord_list_prec = coord_list
#

insert the new coordinate Xn1 after the Xn choosen

    if choice0 == False:  # newco = coordlist[index] if choice 0
        newco = [newX, 0, newZ]
        coord_list.insert(index + 1, newco)
        print("newco ", newco)
    if choice3 == True:
        newco2 = [newX2, 0, newZ2]
        coord_list.insert(index + 2, newco2)
        print("newco2", newco2)
#

move the coordinate after the Xn choosen on X by choice 0,1 or 2

    for coord in coord_list[index + nbadd :]:
        if i == 0:
            coord[0] = coord[0] + (addX + addX2) / 3
        else:
            coord[0] = coord[0] + addX + addX2
        print(addX + addX2)

    dot()
    print("coord_list", coord_list)
    print("nombrecoo", len(coord_list))
    dot()
    print("00000")
#

adding space

    nbcoo = len(coord_list)

    for l in range(0, nbcoo):

        xbisH, ybisH, zbisH = coord_list[l]
        del coord_list[l]
        zbisH = zbisH + 1.45  # space between repetition
        coord_list.insert(l, [xbisH, ybisH, zbisH])
#

repetition courbe bis

    nbrepet = random.randint(3, 10)

    for k in range(0, nbrepet):
        for voixa in range(0, nbinstrument):
            if random.randint(0, 60) == 60:
                list[voixa] = 1
            if i >= 13:
                list[voixa] = 1
#
              extrusion                  #
    for k in range(0, nbrepet):
#

move up every repetition

        for j in range(0, nbcoo):

            xbis, ybis, zbis = coord_list[j]
            del coord_list[j]
            zbis = zbis + 0.5
            coobis = [xbis, ybis, zbis]
            coord_list_bis.insert(j, coobis)

        for voix in range(0, nbinstrument):
#

thickness and offset variable to always have 10cm lenght

            if voix == nbinstrument - 1:
                offset = -1
            elif voix == 0:
                offset = 1
            else:
                offset = 0 + offsetrandom[voix]

            if list[voix] == 1:

                maCourbeCreeBis = curve("mesurebis", coord_list_bis)

                maCourbeCreeBis.select_set(True)  # selection
                sizex = 10 / coord_list[-1][0]  # resize for curves = 10cm
                bpy.ops.transform.resize(value=(sizex, 1, 1))
                bpy.context.view_layer.objects.active = (
                    maCourbeCreeBis  # 'maCourbeCreeBis' is the active object now
                )
                bpy.ops.object.modifier_add(type="SOLIDIFY")
                bpy.context.object.modifiers["Solidify"].thickness = facteur_EP[
                    voix
                ]  # facteur_EP #list_facteur(ii)
                bpy.context.object.modifiers["Solidify"].offset = offset
                bpy.ops.transform.translate(
                    value=(0, (10 / (nbinstrument - 1)) * voix, 0)
                )
                maCourbeCreeBis.select_set(False)

            if k == (nbrepet - 3) and voix == 0:  # signal

                maCourbeCreeBis.select_set(True)
                bpy.context.view_layer.objects.active = maCourbeCreeBis
                bpy.ops.object.modifier_add(type="SOLIDIFY")
                bpy.context.object.modifiers["Solidify"].thickness = (
                    10  # extrusion of the signal
                )
                bpy.context.object.modifiers["Solidify"].offset = 1
                maCourbeCreeBis.select_set(False)

    print(list)
    ibis += 1
    i += 1
#
              pillars                    #
x, y, max_ztotal = max(coord_list_bis, key=lambda item: item[2])
tailleRectx = 0.2
tailleRecty = 0.1

for cix in range(0, 4):
    if cix == 0:
        ix = 1
    elif cix == 3:
        ix = 9
    else:
        ix = (10 / 3.5) * cix + 1
    for ciy in range(1, 6):
        if ciy == 1:
            iy = 1
        elif ciy == 5:
            iy = 9
        else:
            iy = (10 / 5) * ciy - 1
        cube(
            (ix + tailleRectx / 2, iy, max_ztotal / 2),
            (tailleRectx, tailleRecty, max_ztotal),
        )
#

Select cube in the final tower

Ext = maCourbeCreeBis  # la tour
hauteur_cadrage = random.uniform(
    5, (max_ztotal - 5)
)  # pos z of the extraction of the tower
#
def boolean(mod, object):

    bpy.ops.object.modifier_add(type="BOOLEAN")
    bpy.context.object.modifiers["Boolean"].operation = "INTERSECT"
    bpy.context.object.modifiers["Boolean"].solver = mod
    bpy.context.object.modifiers["Boolean"].object = object
#

join everything in one MESH

bpy.ops.object.select_all(action="SELECT")
bpy.context.view_layer.objects.active = maCourbeCreeBis
bpy.ops.object.convert(target="MESH")
bpy.ops.object.join()
bpy.context.active_object.name = "Ext"
#

Cadrage 1 : first cut

cube((5, 5, hauteur_cadrage), (12, 12, 10))
cube1 = get_objects("Cube")[0]
boolean("FAST", Ext)
#

Cadrage 2 : Cube more précise but solver = FAST

cube((5, 5, hauteur_cadrage), (10.2, 10.2, 10))
cube2 = get_objects("Cube.001")[0]
boolean("FAST", cube1)
bpy.ops.object.modifier_apply(modifier="Boolean")
#

Cadrage 3 : solver = EXACT

cube((5, 5, hauteur_cadrage), (10.2, 10.2, 10))
boolean("EXACT", cube2)
#

on applique la fonction boolean cette fois seulement

bpy.ops.object.modifier_apply(modifier="Boolean")
#

replace the cube at 0;0

bpy.ops.transform.translate(value=(0, 0, -hauteur_cadrage + 5))
bpy.ops.object.select_all(action="DESELECT")
#

cleanup

cube1.select_set(True)
bpy.ops.object.delete(use_global=False)

cube2.select_set(True)
bpy.ops.object.delete(use_global=False)

tour = get_objects("Ext")[0]
tour.select_set(True)
bpy.ops.transform.translate(value=(20, 0, 0))
#

closing volumes

bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.editmode_toggle()
#

bpy.ops.mesh.edge_face_add()

bpy.ops.mesh.normals_make_consistent(inside=True)
bpy.ops.mesh.remove_doubles(threshold=0.001)
bpy.ops.mesh.select_all(action="DESELECT")
bpy.ops.object.editmode_toggle()